#include "def.h"
#include "efi.h"

void (*video_printf)(const char *fmt, ...) = (void (*)(const char *fmt, ...))(0x4602CDF0 | 1);
part_dev_t *(*mt_part_get_device)() = (part_dev_t * (*)())(0x4601AB58 | 1);
part_t *(*mt_part_get_partition)(const char *name) = (part_t * (*)(const char *name))(0x460045DC | 1);
void (*arch_clean_invalidate_cache_range)(uint32_t start, uint32_t size) = (void (*)(uint32_t, uint32_t))0x4601AE00;
void (*boot_linux_from_storage)() = (void (*)())(0x46023DCC | 1);
void (*mt_boot_init)() = (void (*)())(0x46024AF4 | 1);
void (*start_apps)() = (void (*)(void))(0x4601C750 | 1);

void print_guid(EFI_GUID *guid)
{
    video_printf("%08X-%04X-%04X-%02X%02X%02X%02X%02X%02X%02X%02X", guid->D1, guid->D2, guid->D3,
                 guid->D4[0], guid->D4[1], guid->D4[2],
                 guid->D4[3], guid->D4[4], guid->D4[5],
                 guid->D4[6], guid->D4[7]);
}

__attribute__((noreturn)) void jump(uint32_t);
__attribute__((noreturn)) void jump64(uint32_t addr, uint32_t arg1, uint32_t arg2, uint32_t arg3);
__attribute__((noreturn)) void jumparch64(uint32_t addr, uint32_t arg1, uint32_t arg2);
void arch_disable_ints(void);
void test64();
uint32_t arm_read_cr1();
void arm_write_cr1(uint32_t);
extern char _agp_test_a64_code[];

void (*arch_disable_cache)(uint32_t) = (void (*)(uint32_t))0x4601AC34;

void test32()
{
    //video_printf("+++++++++++++++++++++++\n");
    for (;;)
        ;
}

__attribute__((section(".text.start"))) __attribute__((noreturn)) void start(void)
{
    part_dev_t *boot_dev = mt_part_get_device();
    // TODO: support booting from recovery partition
    part_t *part = mt_part_get_partition("boot");

    if (boot_dev == NULL || part == NULL)
    {
        video_printf("ERROR");
        for (;;)
            ;
    }

#if 0
    EFI_VOLUME_HEADER Header;
    boot_dev->read(boot_dev, part->start_sect * BLOCK_SIZE + BOOTIMG_OFFSET * BLOCK_SIZE, (uint8_t *)&Header, sizeof(EFI_VOLUME_HEADER), part->part_id);
    uint8_t *signature_u8 = (uint8_t *)&Header.Signature;
    if (signature_u8[0] != '_' || signature_u8[1] != 'F' ||
        signature_u8[2] != 'V' || signature_u8[3] != 'H')
    {
        video_printf("Invalid signature\n");
        for (;;)
            ;
    }
    print_guid(&Header.FileSystemGuid);
#endif

    boot_dev->read(boot_dev, part->start_sect * BLOCK_SIZE + BOOTIMG_OFFSET * BLOCK_SIZE, (uint8_t *)0x41000000, 0x00200000, part->part_id);

    video_printf("\nBooting UEFI ...\n");
    arch_disable_ints();
    // TODO: disable interrupts in controller
    arch_disable_cache(3);
    // disable mmu
    arm_write_cr1(arm_read_cr1() & ~1);
    // disable watchdog
    *((volatile uint32_t *)0x10007000) = (*((volatile uint32_t *)0x10007000) & 0xFFFFFFFE) | 0x22000000;

    jump64(0x41000000, 0x43000000, 0, 1);
    for (;;)
        ;
}